/* 线段树 区间修改
区间修改需要懒标记
* 1.原理
    满二叉树，用一维数组存整棵树

    当前编号是x, 父节点 x >> 1
                左孩子 2x
                右孩子 2x+1

    除最后一层外我们有2n−1个节点
    最后一层最多有2n个节点
    总的节点数不会超过4n−1个节点

* 2.操作
    push_up：由子节点计算父节点
    build：将一段区间初始化成线段树
    modify：修改操作 —— 修改单点或修改区间
    query：查询操作，即查询某一段线段树的信息
    push_down（懒标记、延迟标记）：由父节点计算子节点

* 3.build 构造
    //u：当前节点编号
    //l：当前区间的左端点
    //r：当前区间的右端点
    void build(int u, int L, int R)
    {      
        tree[u].l = L, tree[u].r = R; //将左右端点存入
        if(c == k) return; //如果当前节点为叶子节点，退出
        int mid = L + R >> 1; //计算当前区间中点
        build(u<<1, L, mid);
        build(u<<1|1, mid + 1, R);   
        //push_up(u);//一般会在此时进行push_up操作
        
    }

* 4.递归范围
    查询范围[L, R]
    (1)当前节点[Tl, Tr]整体被包含[L, R] <-> [Tl,Tr]⊂[L,R] <-> L <Tl < Tr < R 
    直接返回当前节点权值

    (2)存在交集, 但不全包含, 则向下递归

    (3)不存在交集, 不存在这种情况

* 5.区间修改 push_down
    懒标记(laz) 给以当前节点为根的子树中的每一个节点(不包含根节点), 加上懒标记
    以区间和为例
    void push_down(int u)
    {
        auto &U = tree[u], &L = tree[L(u)], &R = tree[R(u)];
        L.laz += U.laz;
        L.sum += (L.r-L.l + 1) * U.laz;
        R.laz += U.laz;
        R.sum += (R.r-R.l + 1) * U.laz;
    }
    线段树永远只向下看

* 本题: 
    1.op
        op1 区间修改: 将某一个区间[L, R]+k
        op2 整个区间中，长度大于0的区间总长是多少
    2.线段树中的节点信息
        cnt 当前区间整个被覆盖次数
        len 不考虑祖先节点cnt的前提下，cnt>0的区间总长
    3.
        (1)永远只考虑根节点信息 -> query时不调用push_down
        (2)所有操作均是成对出现，且先+后-
        (3)query中不执行push_down一般不影响答案正确性:query只查询当前根节点,不向下查询
        (4)x,y存在小数，较为稠密，需要离散化
*/

// #define ONLINE_DUDGE
#pragma GCC optimize("O1,O2,O3,Ofast")
#pragma GCC optimize("no-stack-protector,unroll-loops,fast-math,inline")
#pragma GCC target("avx,avx2,fma")
#pragma GCC target("sse,sse2,sse3,sse4,sse4.1,sse4.2,ssse3")

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
// #define int long long
const int N = 2e5+10;
#define L(u) (u << 1) // 左孩子
#define R(u) (u << 1 | 1) // 右孩子
struct Segment //线段
{
    double x, y1, y2;
    int k; // 矩阵左侧/右侧
    bool operator< (const Segment &n)const
    {
        return x < n.x;
    }
}seg[N << 2];

struct Node
{
    int l, r;
    int cnt; //当前区间出现次数
    double len; //长度
}tree[N << 3];

int n;
vector<double> ys; //离散化

int find(double y)
{
    // 需要返回vector 中第一个 >= y 的数的下标
    return lower_bound(ys.begin(), ys.end(), y) - ys.begin();
}

inline void push_up(Node &U,Node &L, Node &R) // 函数重载
{
    // 当前区间被覆盖，计算真实长度 当前区间实际长度=当前线段实际终点-线段起点
    if(U.cnt) U.len = ys[U.r+1]-ys[U.l];
    else if(U.l != U.r){//且不是叶节点
        U.len = L.len + R.len;
    }
    else U.len = 0;
}  


inline void push_up(int u) // u的孩子节点更新u的权值
{
    push_up(tree[u], tree[L(u)], tree[R(u)]);
    
}

inline void build(int u, int l, int r)
{      
    
    if(l == r) {tree[u] = {l, r, 0, 0}; return;}//如果当前节点为叶子节点，退出
    
    tree[u].l = l, tree[u].r = r; //将左右端点存入
    int mid = tree[u].l + tree[u].r >> 1; //计算当前区间中点
    build(L(u), l, mid);
    build(R(u), mid + 1, r);   
    //新建线段树，因为是对每个seg计算过后再用modify加入到线段树中，所以在build时不需要push_up
    //push_up(u);//一般会在此时进行push_up操作
}

inline int query(int u, int l, int r) 
{
    auto &U = tree[u], &L = tree[L(u)], &R = tree[R(u)];
    if(l <= U.l && U.r <= r) return U.len; // 被完全包含

    //push_down(u); // 向下传递懒标记
    int mid = (U.l + U.r) >> 1;
    int sum = 0;
    if(l <= mid) sum = query(L(u), l, r); // 与左区间有交集
    if(r > mid) sum += query(R(u), l, r); // 与右区间有交集
    return sum;
}

inline void modify(int u, int l, int r, int d) // 当前节点 修改区间左右端点 [l, r]出现次数+d
{
    auto &U = tree[u], &L = tree[L(u)], &R = tree[R(u)];
    if(l <= U.l && U.r <= r) {U.cnt += d; push_up(u); return;}// 当前节点的区间被完全包含
    
    //push_down(u); // 向下传递懒标记
    int mid = U.l + U.r >> 1;
    if(l <= mid) modify(L(u), l, r, d);
    if(r > mid) modify(R(u), l, r, d);
    push_up(u);
}

signed main()
{

    #ifdef ONLINE_JUDGE
    ios::sync_with_stdio(false);   
	cin.tie(0);
    #else
    freopen("./in.txt","r",stdin);
    #endif

    int T = 1;
    while(cin >> n, n)
    {
        ys.clear();
        
        for(int i = 0, j = 0; i < n; i++){
            double x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
            seg[j++] = {x1, y1, y2, 1};
            seg[j++] = {x2, y1, y2, -1};
            ys.push_back(y1); ys.push_back(y2); // y轴离散化
        }

        sort(seg, seg+n*2); //线段按x排序
        sort(ys.begin(), ys.end());
        ys.erase(unique(ys.begin(), ys.end()), ys.end());//离散化去重
    
        build(1, 0, ys.size() - 2);

        double res = 0;

        for (int i = 0 ; i < n*2; i ++)
        {
            //根节点的长度即为此时有效线段长度，再*x轴长度即为面积
            res += tree[1].len * (seg[i].x - seg[i - 1].x);
            modify(1, find(seg[i].y1), find(seg[i].y2) - 1, seg[i].k);
        }
            printf("Test case #%d\n", T ++ );
        printf("Total explored area: %.2lf\n\n", res);
    }
    
    return 0;
}
